package com.softlab.logsystem.common.config;

import com.softlab.logsystem.common.GlobalConst;
import com.softlab.logsystem.service.rabbitmq.ConsumerListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author LiXiwen
 * @date 2020/1/7 13:58
 */
@Configuration
public class RabbitmqConfig {
    private static final Logger logger = LoggerFactory.getLogger(RabbitmqConfig.class);

    private final CachingConnectionFactory connectionFactory;
    private final ConsumerListener consumerListener;

    @Autowired
    public RabbitmqConfig(CachingConnectionFactory connectionFactory, ConsumerListener consumerListener) {
        this.connectionFactory = connectionFactory;
        this.consumerListener = consumerListener;
    }

    @Bean
    public DirectExchange logExchange() {
        return new DirectExchange(GlobalConst.EXCHANGE_NAME, true, false);
    }

    @Bean
    public Queue logQueue() {
        return new Queue(GlobalConst.QUEUE_NAME, true);
    }

    @Bean
    public Binding logBinding() {
        return BindingBuilder.bind(logQueue()).to(logExchange()).with(GlobalConst.ROUTING_KEY_NAME);
    }

    @Bean
    public RabbitTemplate rabbitTemplate(){
        connectionFactory.setPublisherConfirms(true);
        connectionFactory.setPublisherReturns(true);
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        // 设置为true时，setReturnCallback获取没有被正确路由的消息
        rabbitTemplate.setMandatory(true);
        /*
        若使用confirm-callback必须要配置publisherConfirms为true
        若使用return-callback必须要配置publisherReturns为true
        每个rabbitTemplate只能有一个confirm-callback和return-callback，如果这里配置了，
        那么写生产者的时候不能再写confirm-callback和return-callback
        使用return-callback时必须设置mandatory为true，或者在配置中设置mandatory-expression的值为true
         */
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause)
                -> logger.info("消息发送成功:correlationData({}),ack({}),cause({})",correlationData,ack,cause));

        /*
         * todo : setConfirmCallback
         * 如果消息没有到exchange,则confirm回调,ack=false
         * 如果消息到达exchange,则confirm回调,ack=true
         * todo : setConfirmCallback
         * exchange到queue成功,则不回调return
         * exchange到queue失败,则回调return(需设置mandatory=true,否则不回回调,消息就丢了)
         */
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey)
                -> logger.info("消息丢失:exchange({}),route({}),replyCode({}),replyText({}),message:{}",exchange,routingKey,replyCode,replyText,message));

        return rabbitTemplate;
    }


    /**
     * 单一消费者配置
     * @return
     */
    @Bean
    public SimpleMessageListenerContainer listenerContainerUserOrder() {
        SimpleMessageListenerContainer container=new SimpleMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);

        // TODO : 并发配置
        container.setConcurrentConsumers(5);
        container.setMaxConcurrentConsumers(20);
        container.setPrefetchCount(5);
        // TODO : 消息确认机制
        container.setQueues(logQueue());
        container.setMessageListener(consumerListener);
        // TODO : 手动应答
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        return container;
    }

}
